package com.web.farme.redis;

import com.web.farme.annotation.EnableRedis;
import com.web.farme.config.DefaultConfig;
import com.web.farme.config.JedisConfig;
import com.web.farme.config.RedisConfig;
import com.web.farme.tool.BeanRegistryUtils;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.Assert;
import redis.clients.jedis.JedisPoolConfig;

public class RedisAutoConfigRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private RedisConfig redisConfig;
    private ConfigurableEnvironment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(EnableRedis.class.getName()));
        Assert.notNull(annotationAttributes, "EnableRedis can not be null");
        this.redisConfig = loadProperties();
        Assert.notNull(this.redisConfig, "redisConfig不能为null");
        //根据注解的属性名去获取需要注入的bean
        Class<? extends RedisCache> redisCache = annotationAttributes.getClass("redisCache");
        Class<? extends RedisSerializer> redisSerializer = annotationAttributes.getClass("redisSerializer");
        BeanRegistryUtils.registerBeanDefinition(registry, redisCache);
        BeanRegistryUtils.registerBeanDefinition(registry, redisSerializer);
        //create RedisClusterConfiguration 集群
        createRedisCluster(registry);
        //create JedisConnectionFactory
        createJedisConnectionFactory(registry);
        //create RedisTemplate
        createRedisTemplate(registry);
    }

    private void createRedisCluster(BeanDefinitionRegistry registry){
        //先定义bean
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(RedisStandaloneConfiguration.class);
        //给这个bean类中的属性赋值
        beanDefinitionBuilder.addPropertyValue("hostName", this.redisConfig.getHost());
        beanDefinitionBuilder.addPropertyValue("port", this.redisConfig.getPort());
        beanDefinitionBuilder.addPropertyValue("password", RedisPassword.of(this.redisConfig.getPassword()));
        //最后注册这个bean到容器中
        BeanRegistryUtils.registerBeanDefinition(registry, beanDefinitionBuilder.getBeanDefinition(), RedisStandaloneConfiguration.class.getSimpleName());
    }

    private void createJedisConnectionFactory(BeanDefinitionRegistry registry){
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(JedisConnectionFactory.class);
        beanDefinitionBuilder.addConstructorArgReference(RedisStandaloneConfiguration.class.getSimpleName());
        beanDefinitionBuilder.addConstructorArgValue(JedisClientConfiguration.builder().usePooling().poolConfig(this.createJedisPoolConfit()).build());
        BeanRegistryUtils.registerBeanDefinition(registry, beanDefinitionBuilder.getBeanDefinition(), JedisConnectionFactory.class.getSimpleName());
    }

    private void createRedisTemplate(BeanDefinitionRegistry registry){
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(RedisTemplate.class);
        beanDefinitionBuilder.addPropertyReference("connectionFactory", JedisConnectionFactory.class.getSimpleName());
        beanDefinitionBuilder.addPropertyValue("keySerializer", new StringRedisSerializer());
        beanDefinitionBuilder.addPropertyValue("valueSerializer", new StringRedisSerializer());
        beanDefinitionBuilder.addPropertyValue("hashKeySerializer", new StringRedisSerializer());
        beanDefinitionBuilder.addPropertyValue("hashValueSerializer", new StringRedisSerializer());
        BeanRegistryUtils.registerBeanDefinition(registry, beanDefinitionBuilder.getBeanDefinition(), RedisTemplate.class.getSimpleName());
    }

    private RedisConfig loadProperties(){return (RedisConfig) getConfig(RedisConfig.class);}



    private JedisPoolConfig createJedisPoolConfit(){

        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        JedisConfig jedisConfig = redisConfig.getJedisConfig();
        jedisPoolConfig.setMaxIdle(jedisConfig.getMaxIdle());
        jedisPoolConfig.setMinIdle(jedisConfig.getMinIdle());
        jedisPoolConfig.setMaxWaitMillis(jedisConfig.getMaxWait());
        jedisPoolConfig.setMaxTotal(jedisConfig.getMaxActive());
        return jedisPoolConfig;

    }

    /**
     * 获取配置文件
     *
     */
    public <T extends DefaultConfig> T getConfig(Class<T> tClass) {
        T resutl;
        T t = null;
        try {
            t = tClass.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        Binder binder = Binder.get(this.environment);
        assert t != null;
        resutl = (binder.bind(t.getConfigKey(), Bindable.of(tClass)).get());
        return resutl;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = (ConfigurableEnvironment) environment;
    }
}
